package modelo.geografia;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import modelo.caracteristicas.ICaracteristica;
import modelo.delictivas.ITrampa;
import modelo.lugaresYPistas.Establecimiento;
import modelo.lugaresYPistas.EstablecimientoCultural;
import modelo.lugaresYPistas.EstablecimientoEconomico;
import modelo.lugaresYPistas.EstablecimientoHistorico;
import modelo.lugaresYPistas.RastroCapital;

public class Capital {

	private String nombre;
	private Coordenada coordenadas;
	private List<Establecimiento> establecimientos;
	private List<Capital> capitalesConectadas;
	private RastroCapital rastro;
	private Boolean ladronEstaEnCiudad;
	
	public Capital(String string, Coordenada unaCoordenada) {
		this.nombre = string;
		this.coordenadas = unaCoordenada;
		this.ladronEstaEnCiudad = false;
		this.establecimientos = new ArrayList<Establecimiento>();
		this.capitalesConectadas = new ArrayList<Capital>();
	}

	public String getNombre() {
		return this.nombre;
	}

	public Coordenada getCoordenadas() {
		return coordenadas;
	}

	public int getCantidadDeEstablecimientos() {
		return this.establecimientos.size();
	}

	public void agregarEstablecimiento(Establecimiento unEstablecimiento) throws Exception {
		if(!this.establecimientos.contains(unEstablecimiento))
			this.establecimientos.add(unEstablecimiento);
		else throw new Exception("Se agrego un establecimiento ya existente en la capital");
	}

	public boolean establecimientoEstaEnCapital(Establecimiento unEstablecimiento) {
		return this.establecimientos.contains(unEstablecimiento);
	}

	public void conectarCapital(Capital unaCapital) throws Exception {
		
		if(this.capitalesConectadas.contains(unaCapital))
			throw new Exception("La capital que intenta conectar ya esta conectada a esta capital");
		
		if(unaCapital.equals(this))
			throw new Exception("Error: Intento conectar" + this.getNombre() + " con si misma");
		
		this.capitalesConectadas.add(unaCapital);
		
		if ( !unaCapital.estaConectadaCon(this) )
			unaCapital.conectarCapital(this);
		
		
	}

	public int getCantidadDeCapitalesConectadas() {
		return this.capitalesConectadas.size();
	}

	public boolean estaConectadaCon(Capital unaCapital) {
		return this.capitalesConectadas.contains(unaCapital);
	}

	public double obtenerDistanciaACapital(Capital otraCapital) {
		return this.coordenadas.getDistanciaA(otraCapital.coordenadas);
	}

	public void setRastro(RastroCapital rastro) {
		this.rastro = rastro;
		
	}

	public RastroCapital getRastro() {
		return this.rastro;
	}

	public boolean tienePistas() {
		boolean hayPista = false;
		Iterator<Establecimiento> itEstablecimientos = establecimientos.iterator();
		while(itEstablecimientos.hasNext())
		{
			hayPista = itEstablecimientos.next().tienePista();
		}
		return hayPista;
	}


public void dejarTrampasEnCapital(List<ITrampa> trampas) {
			
		Establecimiento unEstablecimiento = null;
		Iterator<Establecimiento> itEstablecimientos = this.establecimientos.iterator();
		int i=0;
		
		while(itEstablecimientos.hasNext())
		{
			unEstablecimiento = itEstablecimientos.next();
			if (  (unEstablecimiento.ladronSeEncuentraEnEstablecimiento()) == false )
				unEstablecimiento.setTrampa(trampas.get(i));
			if (i == ( trampas.size()-1) )
				i=0;
			else
				i++;
		}
		
	}
	
	public void dejarRastroHaciaCapitalDestino(RastroCapital rastroDestino) throws Exception {
		
		Iterator<Establecimiento> itEstablecimientos = establecimientos.iterator();
		while(itEstablecimientos.hasNext())
		{
			itEstablecimientos.next().obtenerPistaDeRastro(rastroDestino);
		}
		
	}

	public List<Capital> getCapitalesConectadas() {
		
		return this.capitalesConectadas;
	}
	
	public void esconderLadronEnCapital(){
		
		Random randomGenerator = new Random();
		int i = randomGenerator.nextInt( ( this.establecimientos.size() ));
		Establecimiento lugar = this.establecimientos.get(i);
		lugar.esconderLadronEnEstablecimiento();
		
		this.ladronEstaEnCiudad = true;
		
	}

	public boolean ladronEstaEnCapital() {
		
		if (this.ladronEstaEnCiudad)
			return true;
		
		return false;
	}

	public void agregarCaracteristicaAPistasDeUnEstablecimiento(ICaracteristica unaCaracteristica){
		
		Random randomGenerator = new Random();
		if (this.tieneEstablecimientos() && this.tienePistas())
		{
			this.establecimientos.get(randomGenerator.nextInt(this.establecimientos.size()))
				.agregarCaracteristicaAPistas(unaCaracteristica);
		}
		
	}

	public boolean tieneEstablecimientos() {
		return (this.establecimientos.size() > 0);
	}
	
	public Element serializar(Document doc){

		Element elementoCapital = doc.createElement("Capital");
		elementoCapital.setAttribute("nombre" , this.nombre);
		elementoCapital.setAttribute("ladronEstaEnCiudad", this.ladronEstaEnCiudad.toString());
		
		Element elementoCoordenadas = doc.createElement("Coordenada");
		elementoCapital.appendChild(elementoCoordenadas);
		elementoCoordenadas.setAttribute("coordenadaX", this.coordenadas.getCoordenadaX().toString());
		elementoCoordenadas.setAttribute("coordenadaY", this.coordenadas.getCoordenadaY().toString());
		
		Element elementoEstablecimientos = doc.createElement("Establecimientos");
		elementoCapital.appendChild(elementoEstablecimientos);
		if (this.establecimientos.size() == 3){
			for (Establecimiento establecimiento : this.establecimientos) {
				   elementoEstablecimientos.appendChild(establecimiento.serializar(doc));
			}
		}
		
		Element elementoRastro = doc.createElement("Rastro");
		elementoCapital.appendChild(elementoRastro);
		if(this.rastro != null){
			elementoRastro.appendChild(this.rastro.serializar(doc));
		}
		
		return elementoCapital;
	}
	
	public static Capital hidratar(Element capitalSerializada) throws Exception{
		
		String nombre = capitalSerializada.getAttribute("nombre");
		boolean ladronEnCapital = false;
		String ladronEnCapitalString = capitalSerializada.getAttribute("ladronEstaEnCiudad");
		if(ladronEnCapitalString.equals("true"))
			ladronEnCapital = true;

		Coordenada coord = new Coordenada(Integer.parseInt(capitalSerializada.getElementsByTagName("Coordenada").item(0).getAttributes().item(0).getTextContent()) ,
				Integer.parseInt(capitalSerializada.getElementsByTagName("Coordenada").item(0).getAttributes().item(1).getTextContent()) );
		
		Capital capital = new Capital(nombre , coord);
		capital.ladronEstaEnCiudad = ladronEnCapital;
		if (capitalSerializada.getElementsByTagName("Establecimientos").item(0).getChildNodes().getLength() == 3){
			
			for (int i = 0 ; i < 3 ; i ++){
				Node establecimiento = capitalSerializada.getElementsByTagName("Establecimientos").item(0).getChildNodes().item(i);
				if (establecimiento.getNodeName().equals("EstablecimientoCultural")){
					capital.agregarEstablecimiento(EstablecimientoCultural.hidratar((Element)establecimiento));
				}
				else if (establecimiento.getNodeName().equals("EstablecimientoHistorico")){
					capital.agregarEstablecimiento(EstablecimientoHistorico.hidratar((Element)establecimiento));
				}
				else if (establecimiento.getNodeName().equals("EstablecimientoEconomico")){
					capital.agregarEstablecimiento(EstablecimientoEconomico.hidratar((Element)establecimiento));
				}
			}
		}
		
		if( capitalSerializada.getElementsByTagName("Rastro").item(0).getChildNodes().getLength() == 1){
			
			capital.setRastro(RastroCapital.hidratar(capitalSerializada.getElementsByTagName("Rastro").item(0).getFirstChild()));
		}
		
		Mapa mapa = Mapa.obtenerMapa();
		Capital capitalExistente = mapa.obtenerCapitalLlamada(capital.getNombre());
		if(capitalExistente != null)
		{
			capitalExistente.copiar(capital);
			return capitalExistente;
		}
		else	
			return capital;
	}

	private void copiar(Capital capital) {
		this.copiarEstablecimientosDe(capital);
		this.coordenadas = capital.coordenadas;
		this.ladronEstaEnCiudad = capital.ladronEstaEnCiudad;
	}

	private void copiarEstablecimientosDe(Capital capital) {
		if(this.establecimientos.size() == capital.establecimientos.size())
		{
			for(int i = 0; i<this.establecimientos.size(); i++)
			this.establecimientos.get(i).copiar(capital.establecimientos.get(i));
		}
		else
			this.establecimientos = capital.establecimientos;
	}

	public List<Establecimiento> getEstablecimientos() {
		return this.establecimientos;
	}

	public void reiniciar() {
		this.ladronEstaEnCiudad = false;
		for(Establecimiento establecimiento : establecimientos)
		{
			establecimiento.reiniciar();
		}
	}
}
